from transformers import AutoTokenizer, AutoModel
import os
import re
from flask import request, jsonify, Blueprint, stream_with_context
import json
import random
import math
import datetime
from collections import Counter
from sqlalchemy import inspect

from ..util import success_res, fail_res
from ..model import Question, AutoQuestion, AutoScore, SingleChoiceQuestion, FillBlankQuestion, MultiChoiceQuestion, SubjectiveQuestion, DissertationQuestion
from .. import db, app
from ..llm import llm_model, model_name

chatglm_bp = Blueprint('chatglm', __name__)
# 1选择；3填空（3.1，大模型出题文本比对校验效果较低，换3.2大模型出题大模型答题校验效果适中，3大模型出词正则出题）；4简答题；5论述题
question_formmaters = {
    1: """
        你是一个专业的单选题出题老师，基于给定的文本内容，出3-5个选择题，其中抠选出信息是该文本片段中包含的关键信息，包括时间、地点、组织机构、关键词、人物、武器装备、基地等重要信息。
        严格遵守下列要求：
        返回标准的json格式，注意是多个选择题样本，因此返回数组类型，格式如下：[{{"问题": question, "选项": {{options}}, "答案": answer}}, {{"问题": question, "选项": {{options}}, "答案": answer}}]
        答案是单个字母字符，只能是"A"、"B"、"C"或"D"
        问题形式，要么用符号【】替代问题中抽取的关键信息，要么用疑问句对抽取的关键信息进行提问
        
        例如：
        文本内容：日向级护卫舰（ひゅうが型護衛艦）是日本海上自卫队旗下的一个直升机护卫舰（Helicopter Destroyer, DDH）舰级。本级舰共两艘，首舰日向号于2006开工，2009年3月18日就役。二号舰伊势号，这一舰名源自日本古代的伊势国，即现在的三重县。于2011年3月16日就役。
        选择题：
        [
            {{
                "问题": "日向级护卫舰是【】旗下的一个直升机护卫舰（Helicopter Destroyer, DDH）舰级。",
                "选项": {{"A": "美国海军", "B": "日本海上自卫队", "C": "韩国海军", "D": "中国海军"}},
                "答案": "B"
            }},
            {{
                "问题": "日向级护卫舰本级舰共两艘，首舰【】于2006开工，2009年3月18日就役。",
                "选项": {{"A": "羽黑号", "B": "舰伊势号", "C": "日向号", "D": "摩耶号"}},
                "答案": "C"
            }}
        ]
        
        以下述文本内容出题
        文本内容：{content}
        仅输出选择题
    """,
    2: """
        你是一个专业的出题专家，基于给定的文本内容，给出 1 或 2 道多选题，若为两道题，则答案不要相同。
        步骤 1：从文本核心信息中截取 4 个相关内容片段作为选项基础；
        步骤 2：检查每个选项是否存在主宾语缺失问题，对语法不完整的选项补充必要信息，确保表述通顺准确；
        步骤 3：在每道题中，选择 1-2 个选项，将其关键信息修改为与原文意思相反或差别较大的错误内容；
        步骤 4：确定 4 个选项中的正确选项后，按以下格式输出：每个选择题需包含题目、"A"、"B"、"C"或"D" 四个选项，正确答案为 2-3 个选项组成的数组。
        样例：
        给定文本材料：大模型是参数规模大、结构复杂的机器学习模型，通常由深度神经网络构建，拥有数十亿甚至数千亿参数，适用于数据量大、计算资源充足的场景（如云端计算）；其核心特征是具备 “涌现能力”，能从数据中自动学习高层次特征，这是与小模型的关键区别。小模型参数少、层数浅，轻量高效，适用于计算资源有限的场景（如移动端、嵌入式设备）。
        输出多选题：
        [
            {{
                "题目": "根据文本内容，关于大模型与小模型的描述正确的有哪些？",
                "选项": {{
                    "A": "大模型通常由深度神经网络构建，拥有数十亿甚至数千亿参数",
                    "B": "小模型因参数多、层数深，适用于云端计算等资源充足场景",
                    "C": "大模型的 “涌现能力” 指能从数据中自动学习高层次特征，是与小模型的关键区别",
                    "D": "小模型轻量高效，适用于移动端、嵌入式设备等计算资源有限场景"
                }},
                "答案": ["A", "C", "D"]
            }}
        ]

        以下述文本内容出题
        文本内容：{content}
        仅输出多选题
    """,
    3: """
        你是一个专业的填空题出题老师，基于给定的文本内容，分解出1-5个重要的文本片段，从文本片段中抽取出关键信息，包括时间、地点、组织机构、关键词、人物、武器装备、基地等关键信息。
        返回标准的json格式，注意是多个关键信息样本，因此返回数组类型。
        关键信息是通过文本片段做关键词抽取，从中选择最关键的1-3个词语，词语不做任何改动。文本片段中，删除关键词，原位置用符号【】代替。
        格式如下：[{{"文本片段": content, "关键信息": [word]}}]

        例如：
        文本内容：日向级护卫舰是日本海上自卫队旗下的一个直升机护卫舰舰级。本级舰共两艘，首舰日向号于2006开工，2009年3月18日就役，二号舰伊势号，这一舰名源自日本古代的伊势国，即现在的三重县。于2011年3月16日就役。
        填空题：
        [
            {{
                "文本片段": "日向级护卫舰是【】旗下的一个直升机护卫舰舰级。",
                "关键信息": ["日本海上自卫队"]
            }},
            {{
                "文本片段": "日向级护卫舰本级舰共两艘，首舰【】于2006开工，2009年3月18日就役，二号【】，于2011年3月16日就役。",
                "关键信息": ["日向号", "舰伊势号"]
            }}
        ]

        以下述文本内容出题
        文本内容：{content}
        仅输出填空题
    """,
    4: """
        你是一个专业的主观题出题老师，基于给定的文本内容，出1个主观题。
        严格遵守下列要求：
        1. 问题应该是开放性的，需要学生进行分析、论述、评价或解释
        2. 每道题满分10分
        3. 为每道题提供不同质量的参考答案：满分答案(10分)、良好答案(8分)、一般答案(6分)、较差答案(4分)
        4. 返回标准的JSON格式，格式如下：[{{"问题": question, "满分": 10, "答案": [{{"内容": answer_content, "得分": score}}]}}]
        5. 答案应该体现不同的分析深度和完整性，得分从高到低依次为：10分、8分、6分、4分
        
        例如：
        文本内容：人工智能技术的发展正在改变人类的生活方式，从智能手机到自动驾驶汽车，AI技术已经渗透到各个领域。然而，AI技术的发展也带来了一些挑战，包括就业问题、隐私保护、算法偏见等。
        
        主观题：
        [
            {{
                "问题": "结合材料，分析人工智能技术发展对社会的影响，并提出应对挑战的建议。",
                "满分": 10,
                "答案": [
                    {{
                        "内容": "人工智能技术发展对社会产生了深远影响。积极方面：1）提高了生活便利性，如智能手机、自动驾驶等技术改善了人们的生活体验；2）推动了产业升级和效率提升；3）为科研、医疗等领域带来突破性进展。消极方面：1）就业结构性变化，部分传统岗位被替代；2）个人隐私面临新的威胁；3）算法偏见可能加剧社会不公。应对建议：1）加强AI伦理规范建设；2）完善相关法律法规；3）推动教育体系改革，培养适应AI时代的人才；4）建立公平透明的算法审核机制；5）加强国际合作，共同应对AI发展挑战。",
                        "得分": 10
                    }},
                    {{
                        "内容": "人工智能技术对社会有积极和消极两方面影响。积极影响包括让生活更便利，如智能手机和自动驾驶技术的应用。消极影响包括可能造成失业、隐私泄露和算法不公平等问题。为应对这些挑战，需要制定相关法律法规，加强技术监管，同时提高公众的AI素养。",
                        "得分": 8
                    }},
                    {{
                        "内容": "AI技术发展改变了我们的生活，带来了便利但也有问题。好处是技术让生活更方便，坏处是可能导致失业和隐私问题。应该通过法律来规范AI的发展。",
                        "得分": 6
                    }},
                    {{
                        "内容": "人工智能技术有好有坏，给生活带来便利但也有挑战，需要解决这些问题。",
                        "得分": 4
                    }}
                ]
            }}
        ]
        
        以下述文本内容出题
        文本内容：{content}
        仅输出主观题
    """,
    5: """
        你是一个专业的论述题出题老师，基于给定的文本内容，出1个论述题。
        严格遵守下列要求：
        1. 问题应该是具有深度的，需要学生进行深入剖析、全面论述、辩证评价或系统阐释
        2. 每道题满分15分
        3. 为每道题提供不同质量的参考答案：满分答案(15分)、良好答案(12分)、一般答案(9分)、较差答案(6分)
        4. 返回标准的JSON格式，格式如下：[{{"问题": question, "满分": 15, "答案": [{{"内容": answer_content, "得分": score}}]}}]
        5. 答案应该体现不同的论述深度、逻辑完整性和论证力度，得分从高到低依次为：15分、12分、9分、6分

        例如：
        文本内容：环境保护与经济发展的关系是当代社会面临的重要课题。一方面，经济发展为环境保护提供了物质基础和技术支持；另一方面，过度追求经济增长可能导致生态破坏，反过来制约经济的可持续发展。协调二者关系，实现绿色发展，是各国共同的目标。

        论述题：
        [
            {{
                "问题": "结合材料，论述环境保护与经济发展的辩证关系，并阐述实现绿色发展的路径。",
                "满分": 15,
                "答案": [
                    {{
                        "内容": "环境保护与经济发展是相互依存、相互制约的辩证统一关系。首先，经济发展是环境保护的基础：经济增长能为环境治理提供资金和技术保障，只有具备一定的经济实力，才能投入更多资源用于污染治理、生态修复和环保技术研发，推动环保产业发展。其次，环境保护是经济可持续发展的前提：良好的生态环境是经济发展的物质条件，破坏环境会导致资源枯竭、生态失衡，最终制约经济的长远发展。实现绿色发展的路径包括：1. 转变经济发展模式，推动产业结构升级，发展低碳经济、循环经济；2. 加强环境立法与执法，建立健全生态环境保护制度，明确各方责任；3. 加大环保科技研发投入，推广应用节能减排技术；4. 提升全社会的环保意识，倡导绿色消费、绿色生活方式；5. 加强国际合作，共同应对全球性环境问题，分享绿色发展经验与技术。",
                        "得分": 15
                    }},
                    {{
                        "内容": "环境保护与经济发展既相互联系又相互影响。经济发展能为环境保护提供支持，比如有了资金可以更好地治理环境；而保护环境也能为经济发展提供保障，若环境被破坏，经济发展会受到限制。实现绿色发展，需要改变经济增长方式，发展环保产业，同时完善环保法规，提高人们的环保意识。",
                        "得分": 12
                    }},
                    {{
                        "内容": "环境保护和经济发展有关系，经济发展好了能帮助保护环境，环境好了也对经济发展有好处。要实现绿色发展，就得注意保护环境，发展相关产业。",
                        "得分": 9
                    }},
                    {{
                        "内容": "环境保护与经济发展之间有联系，两者都很重要。实现绿色发展需要关注环境和经济。",
                        "得分": 6
                    }}
                ]
            }}
        ]

        以下述文本内容出题
        文本内容：{content}
        仅输出论述题
    """
}

answer_formatters = {
    1: """
        你是一个专业的选择题答题学生，基于给定的文本内容和选项，回答选择题，请直接给出答案，返回答案必须是选项主键大写字母字符串。

        例如：
        文本内容：卡-26/126是为农业和其他空中作业研制的多功能直升机，1965年首飞。引人注目的是它的驾驶舱后面安装了可互换模块，可针对客运/货运、空中救护、消防及其他用途而进行调换。1970年，卡-26/126直升机在前苏联开始了它的服役生涯。该机型共计交付了超过850架。
        问题：卡-26/126是为【】和空中作业研制的多功能直升机。
        选项: {{"A": "重工业", "B": "轻工业", "C": "农业", "D": "旅游业"}}
        答案："C"
        问题：1970年，卡-26/126直升机在【】开始了它的服役生涯。
        选项: {{"A": "德国", "B": "前苏联", "C": "俄罗斯", "D": "乌克兰"}}
        答案："B"

        文本内容：{content}
        选项：{options}
        问题：{question}
        仅输出答案：
        """,
    3: """
        你是一个专业的填空题答题学生，基于给定的文本内容，回答填空题，请直接给出答案，返回答案可能是多个字符串，所以必须是数组类型，返回格式严格遵守JSON标准格式。
        格式如下：["答案1", "答案2"]

        例如：
        文本内容：卡-26/126是为农业和其他空中作业研制的多功能直升机，1965年首飞。引人注目的是它的驾驶舱后面安装了可互换模块，可针对客运/货运、空中救护、消防及其他用途而进行调换。1970年，卡-26/126直升机在前苏联开始了它的服役生涯。该机型共计交付了超过850架。
        问题：卡-26/126是为【】和【】研制的多功能直升机。
        答案：["农业", "空中作业"]
        问题：1970年，卡-26/126直升机在【】开始了它的服役生涯。
        答案：["前苏联"]

        文本内容：{content}
        问题：{question}
        仅输出答案：
        """
}

subjective_question_prompt_example_formmater = """
        示例{ques_idx}：
        输入
        {input_example}
        输出
        {{"评分理由": {reason}, "得分": {score}}}
        """

subjective_question_formmaters = {
    4: """
        你是一个专业的判分老师，基于输入的标准答案，通过计算任务的标准答案和待判答案之间的相似性，标准答案的内容是否都在待判答案中提及，对待判答案给出评分理由和得分，其中得分满分是10分，判分得分不得超过10分。
        执行步骤：
        解析任务中输入的JSON数据，得到标准答案与待判答案和Rouge指标，根据Rouge指标，判定得分范围；
        判分原则：
        分析标准答案的主要内容（步骤或流程中的纯数字除外），判断主要内容是否在待判答案中涵盖：
            Rouge>0.9，限定范围可得10分；
            0.5<Rouge<0.9，限定范围在[5-9]分；
            0.1<Rouge<0.5，限定范围在[2-4]分；
            Rouge<0.1，限定范围仅得1分；
            待判答案为空，限定范围仅得0分。
        待判答案的Rouge指标越高，得分越高，但不能超过限定范围；
        输出格式是标准的json格式，包括评分理由和得分，评分理由是字符串形式，得分是数值形式。格式如下：{{"评分理由": "理由", "得分": score}}
        
        输入
        {input_example}
        输出评分理由和得分
    """,
}

auto_judge_fomatters = {
    1: """
        你是一个专业的自动判分系统，基于给定的回答内容和标准答案，进行 10 次独立判分，然后综合考虑得出最终得分。
        严格遵守下列要求：
        1. 每次判分总分为 100 分，根据回答在完整性、逻辑性、大模型得分要点、相似度得分要点这几个方面的表现，按照对应 0.2、0.2、0.4、0.2 的权重进行加权计算得出每次的判分。
        2. 完整性得分依据回答是否涵盖了标准答案所有关键信息来评定，满分为 100 分。
        3. 逻辑性得分依据回答内容的条理是否清晰、论证是否合理来评定，满分为 100 分。
        4. 大模型得分要点得分依据回答是否准确体现大模型相关的专业要点来评定，满分为 100 分。
        5. 相似度得分要点得分依据回答与标准答案的文本相似度来评定，满分为 100 分。
        最终得分是 10 次判分的平均值。
        返回标准的 JSON 格式，格式如下：{{"10 次判分": [{{"完整性得分": 完整性得分_1, "逻辑性得分": 逻辑性得分_1, "大模型得分要点得分": 大模型得分要点得分_1, "相似度得分要点得分": 相似度得分要点得分_1, "本次得分": 本次得分_1}}, {{"完整性得分": 完整性得分_2, "逻辑性得分": 逻辑性得分_2, "大模型得分要点得分": 大模型得分要点得分_2, "相似度得分要点得分": 相似度得分要点得分_2, "本次得分": 本次得分_2}}, …, {{"完整性得分": 完整性得分_10, "逻辑性得分": 逻辑性得分_10, "大模型得分要点得分": 大模型得分要点得分_10, "相似度得分要点得分": 相似度得分要点得分_10, "本次得分": 本次得分_10}}], "最终得分": 最终得分}}
        例如：
        回答内容：“这是一个较为完整、逻辑较清晰且在一定程度上体现大模型专业要点的回答，与标准答案有一定相似性。”
        标准答案：“这是一个完整、逻辑清晰且准确体现大模型专业要点的回答，与标准答案相似。”
        {{
            "10 次判分": [
            {{"完整性得分": 80, "逻辑性得分": 80, "大模型得分要点得分": 70, "相似度得分要点得分": 70, "本次得分": 74}},
            {{"完整性得分": 85, "逻辑性得分": 85, "大模型得分要点得分": 75, "相似度得分要点得分": 75, "本次得分": 79}},
            {{"完整性得分": 80, "逻辑性得分": 80, "大模型得分要点得分": 70, "相似度得分要点得分": 70, "本次得分": 74}},
            {{"完整性得分": 85, "逻辑性得分": 85, "大模型得分要点得分": 75, "相似度得分要点得分": 75, "本次得分": 79}},
            {{"完整性得分": 80, "逻辑性得分": 80, "大模型得分要点得分": 70, "相似度得分要点得分": 70, "本次得分": 74}},
            {{"完整性得分": 85, "逻辑性得分": 85, "大模型得分要点得分": 75, "相似度得分要点得分": 75, "本次得分": 79}},
            {{"完整性得分": 80, "逻辑性得分": 80, "大模型得分要点得分": 70, "相似度得分要点得分": 70, "本次得分": 74}},
            {{"完整性得分": 85, "逻辑性得分": 85, "大模型得分要点得分": 75, "相似度得分要点得分": 75, "本次得分": 79}},
            {{"完整性得分": 80, "逻辑性得分": 80, "大模型得分要点得分": 70, "相似度得分要点得分": 70, "本次得分": 74}},
            {{"完整性得分": 85, "逻辑性得分": 85, "大模型得分要点得分": 75, "相似度得分要点得分": 75, "本次得分": 79}}
            ],
            "最终得分": 76.5
        }}
        以下述回答内容和标准答案进行判分
        回答内容：{answer_content}
        标准答案：{standard_answer}
        仅输出判分结果的 JSON 格式内容
    """
}

def subject_example_prompt_formater(x, y, z): return json.dumps(
    {'标准答案': x, '待判答案': y, 'Rouge': z}, ensure_ascii=False)


subjective_question_response_re_formater = r'(?s).*?"评分理由"\s*:\s*"((?:[^"\\]|\\.)*)"\s*,\s*"得分"\s*:\s*(\d+(?:\.\d+)?).*' #'\{[\n ]*"?评分理由"?: "?(.*)"?[,\n ]*"?得分"?: "?([\d\.]*)[\n 分]*"?[\n ]*\}'


# 出题的答案检测
def check_question_expert_status(ques_tagid, content, ques_title, ques_answer: str, ques_options: str):
    total_count = 1
    accur_count = 0
    flag = False
    expert_status = 0
    try:
        # print("try", answer_formatters[ques_tagid], flush=True)
        input = ''
        if ques_tagid == 1:
            input = answer_formatters[ques_tagid].format(
                content=content, question=ques_title, options=ques_options)
        elif ques_tagid == 3:
            input = answer_formatters[ques_tagid].format(
                content=content, question=ques_title)
        # print(input, flush=True)
        if input:
            response = llm_model.chat(input)
            # print('ques_answer in check_question_expert_status', ques_answer, flush=True)
            print('response in check_question_expert_status', response, flush=True)
            if ques_tagid == 1:
                re_ques_answer = re.match('^"?(.{1})"?$', ques_answer)
                re_expert_answer = re.match('^[答案："]*(.{1})"?$', response)
                # print('re_ques_answer', re_ques_answer, flush=True)
                # print('re_expert_answer', re_expert_answer, flush=True)
                if re_ques_answer and re_expert_answer:
                    ques_answer_option = re_ques_answer.group(1)
                    expert_answer_option = re_ques_answer.group(1)
                    expert_status = accur_count = ques_answer_option == expert_answer_option
                    # print('expert_status', ques_answer_option == expert_answer_option, flush=True)
                else:
                    expert_status = 0
            elif ques_tagid == 3:
                print('*' * 5, ques_answer, flush=True)
                ques_answer = json.loads(ques_answer)
                print(ques_answer, flush=True)
                expert_answer = json.loads(response)
                total_count = len(ques_answer)
                while ques_answer and expert_answer:
                    ques_answer_item = ques_answer.pop(0)
                    expert_answer_item = expert_answer.pop(0)
                    if ques_answer_item == expert_answer_item:
                        accur_count += 1
                expert_status = accur_count == total_count
            flag = True
        else:
            print("答案检测input为空", flush=True)
    except Exception as e:
        print(f"答案检测异常: {str(e)}", flush=True)
    return flag, expert_status, accur_count, total_count


# json格式
def llm_create_objective_question(content, ques_tagid, batch=False, datacollection_name=None):
    """
    params: content: 题干来源材料
    params: ques_tagid: 题型: 1选择3填空
    """
    # print(ques_tagid, question_formmaters[ques_tagid], flush=True)
    input = question_formmaters[ques_tagid].format(content=content)
    print(' - ' * 20, flush=True)
    print('input：', input, flush=True)
    response = llm_model.chat(input)

    flag, data = True, []
    try:
        print('客观题：', response, flush=True)
        if ques_tagid == 1:
            question_list = re.findall(
                '"问题":\s?"(.*)",[\s\n]*"选项":\s?{?(.*)}?,[\s\n]*{?"答案":\s?"(.*)"', response)
    
            # print('-*- ' * 10, flush=True)
            # print('question_list is', question_list, flush=True)

            if question_list:
                for ques_item in question_list:
                    # print('-' * 10, len(ques_item), flush=True)
                    # print(ques_item, flush=True)

                    ques_title = ques_item[0]
                    ques_option_options = re.findall(
                        '"(\w){1}":\s"(.+?)"', ques_item[1])
                    ques_answer = ques_item[2]

                    # print('+' * 10, flush=True)
                    # print(ques_option_options, flush=True)

                    ques_options = {option[0]: option[1]
                                    for option in ques_option_options}
                    ques_options_str = json.dumps(
                        ques_options, ensure_ascii=False)

                    ques_word_tags = ['哪', '什么', '?', '？', '【】', '()', '（ ）']

                    def check_is_question(content):
                        for tag in ques_word_tags:
                            if tag in content:
                                return True
                        return False

                    status = 0
                    re_ques_answer = re.match('^"?(.{1})"?$', ques_answer)
                    if check_is_question(ques_title) and re_ques_answer:
                        ques_answer = re_ques_answer.group(1)
                        status = 1

                    if batch:
                        # 主观题入库，在question.py的create_question接口中
                        _, expert_status, accur_count, total_count = check_question_expert_status(ques_tagid, content,
                                                                                                  ques_title,
                                                                                                  ques_answer,
                                                                                                  ques_options_str)

                        # ques_title += '\n' + ques_options_str

                        try:
                            aq = AutoQuestion(content=content, output_chat=response, ques_tagid=ques_tagid,
                                              output_title=ques_title, output_options=ques_options,
                                              output_answer=ques_answer,
                                              output_accur_count=accur_count, output_total_count=total_count,
                                              expert_status=expert_status, status=status,
                                              model_name=model_name, datacollection_name=datacollection_name)
                            db.session.add(aq)
                            db.session.commit()
                        except:
                            db.session.rollback()

                    data.append({'ques_title': ques_title, 'ques_answer': ques_answer, "ques_options": ques_options,
                                 'ques_tagid': ques_tagid})
            else:
                flag, data = False, "题目完整度不足"
        elif ques_tagid == 3:
            context_answers_list = re.findall(
                '"文本片段":\s?"(.*)",\n*\s*"关键信息":\s?\[(.*)\]', response)
            if context_answers_list:
                for context_answers_item in context_answers_list:
                    ques_title_content = context_answers_item[0]
                    answers_str = context_answers_item[1]
                    ques_answer_words = re.findall('"(.+?)"', answers_str)

                    ques_answer_idx = []
                    for ques_answer_word in ques_answer_words:
                        word_split = re.split(
                            ques_answer_word, ques_title_content, 1)
                        if len(word_split) > 1:
                            ques_answer_idx.append(
                                {'word': ques_answer_word, 'idx': ques_title_content.index(ques_answer_word)})
                            ques_title_content = '【】'.join(word_split)

                    ques_answer = [i['word'] for i in sorted(
                        ques_answer_idx, key=lambda x: x['idx'])] if len(ques_answer_idx) else ques_answer_words
                    ques_answer_str = json.dumps(
                        ques_answer, ensure_ascii=False)

                    if batch:
                        status = 1 if '【】' in ques_title_content else 0
                        accur_count = len(ques_answer)
                        total_count = len(ques_answer_words)
                        expert_status = 1 if len(ques_answer) == len(
                            ques_answer_words) else 0

                        try:
                            aq = AutoQuestion(content=content, output_chat=response, ques_tagid=ques_tagid,
                                              output_title=ques_title_content,
                                              output_answer=ques_answer_str,
                                              output_accur_count=accur_count, output_total_count=total_count,
                                              expert_status=expert_status, status=status,
                                              model_name=model_name, datacollection_name=datacollection_name)
                            db.session.add(aq)
                            db.session.commit()
                        except:
                            db.session.rollback()

                    data.append(
                        {'ques_title': ques_title_content, 'ques_answer': ques_answer, 'ques_tagid': ques_tagid})
            else:
                flag, data = False, "题目完整度不足"
    except Exception as e:
        print(
            f"Exception in llm_create_objective_question: {str(e)}", flush=True)
        flag, data = False, "自动出题异常"

    return flag, data


@chatglm_bp.route('/content_create_question', methods=['POST'])
def content_create_question():
    content = request.json.get('content', '')
    ques_tagid = request.json.get('ques_tagid', 1)
    if question_formmaters.get(ques_tagid, ''):
        flag, res = llm_create_objective_question(content, ques_tagid)
        if flag:
            return success_res(data=res)
        else:
            return fail_res(msg=f"题目创建出错，{res}")
    else:
        return fail_res(msg="没有该题型自动出题")


'''
    def generate():
        yield 'Hello '
        yield request.args['name']
        yield '!'

    return question_bp.response_class(stream_with_context(generate()))
'''

gaokao_file = os.path.join(os.getcwd(), 'static', 'bench.json')
gaokao_questions = []
with open(gaokao_file, 'r', encoding='utf-8') as f:
    gaokao_data = json.loads(f.read())
    gaokao_questions = gaokao_data['example']


def get_ques_content(content):
    # print(content, flush=True)
    return re.search('[0-9]*[\.|．](（\s*[0-9]*分）)?([\S\s]*)', content).group(2)


def sub_signal_blank(context):
    context = re.sub('[,./?:;"\'\{\}\s!@#$%&\(\)，。、？：；“‘’”【】！]', '', context)
    return context


def rouge(output_answer, stand_answer):
    candidate_tokens = list(output_answer)
    reference_tokens = list(stand_answer)

    candidate_tokens = [''.join(candidate_tokens[i:i + 2])
                        for i in range(len(candidate_tokens) - 2)]
    reference_tokens = [''.join(reference_tokens[i:i + 2])
                        for i in range(len(candidate_tokens) - 2)]

    # print(candidate_tokens, flush=True)
    # print(reference_tokens, flush=True)
    # Count bigram occurrences in both summaries
    candidate_counter = Counter(candidate_tokens)
    reference_counter = Counter(reference_tokens)

    # Calculate the intersection of counts between candidate and reference
    overlap = candidate_counter & reference_counter
    correct_count = sum(overlap.values())
    # print(correct_count, flush=True)
    return 2 * correct_count / (len(reference_tokens) + len(candidate_tokens))

    # conv = [i for i in output_answer if i in stand_answer]
    # # print(len(conv), len(output_answer), len(stand_answer), flush=True)
    # return 2 * len(conv) / (len(output_answer) + len(stand_answer))


def get_chatglm_score_by_gaokao(ques_title, standard_answer, model_output):
    # print(sub_signal_blank(standard_answer), flush=True)
    # print(sub_signal_blank(model_output), flush=True)
    if sub_signal_blank(standard_answer) == sub_signal_blank(model_output):
        return 1, 10, '与标准答案基本一致，可获满分', ''
    questions = [
        # {
        # "information": "家庭农场是指以家庭成员为主要劳动力，从事农业规模化、集约化、商品化生产经营，并以农业收入为家庭主要收入来源的农业组织形式。2013年中央“一号文件”首次提出发展家庭农场。2019年中央“一号文件”提出“全面深化农村改革，激发乡村发展活力”，并再次强调要坚持家庭经营基础性地位，突出培育家庭农场等新型农业经营主体。\n近年来，我国家庭农场发展迅速，数量已超过87.7万户。据2019年农业农村部信息：\n我国家庭农场大多由小农户升级而来，经营规模在20～200亩之间；家庭农场主要从事种植业、养殖业和种养结合，其中种植类农场有56.1 % 采用了喷灌技术，养殖类农场有近80 % 进行了粪便资源化、综合循环利用和无害化处理；在不少家庭农场中，父辈负责生产，子女负责营销，经营的农产品有以自己名字命名的品牌；全国有36.9 % 的家庭农场加入了农民合作社，参与和分享农机、良种、技术、订单等服务。",
        # "question": "结合材料并运用经济知识，说明发展家庭农场对于激发乡村经济活力的积极作用。",
        # "standard_answer": "①促进农村土地流转与规模经营，提高农业劳动生产率；\n②促使先进农业技术应用，提高农产品产量和品质；\n③推进农业品牌化、专业化经营，增强农产品市场竞争力；\n④拓宽农民合作社服务范围，推动乡村产业融合发展。",
        # "answer": "发展家庭农场对于激发乡村经济活力的积极作用主要体现在以下几个方面：1.提高农业生产效率，推动农业规模化、集约化、商品化。2.带动农业技术的更新和提高，提高农业生产效率，降低农业生产成本，提高农产品的质量和产量。3.带动农产品的品牌化，提高农产品的附加值，增加农业收入。4.推动农民合作社的发展，通过共享农机、良种、技术、订单等服务，提高农业生产效率，降低农业生产成本，提高农业收入。",
        # "reason": "待判答案涵盖了标准答案中的四个主要点，即提高农业生产效率、促使先进农业技术、推进农产品品牌化以及推动乡村产业融合发展。学生的答案不仅重述了材料中的信息，还结合了经济知识，对家庭农场激发乡村经济活力的积极作用进行了全面的说明。因此，学生的答案符合标准答案的要求，应给予满分。",
        # "score": 10
        # }, {
        #     "information": "新型冠状病毒肺炎是近百年来人类遭遇的影响范围最广的全球性大流行病，对全世界是一次严重危机和严峻考验。人类生命安全和健康面临重大威胁。\n面对突如其来、来势汹汹的疫情天灾，中国果断打响疫情防控阻击战，把人民生命安全和身体健康放在第一位，采取最全面最严格最彻底的防控措施，取得了抗击疫情重大战略成果。中国始终秉持人类命运共同体理念，肩负大国担当，同其他国家并肩作战、共克时艰，毫无保留同各方分享防控和救治经验，尽己所能向国际社会提供人道主义援助，支持全球抗击疫情。\n2020年3月26日，国家主席习近平出席二十国集团领导人特别峰会并发表《携手抗疫共克时艰》讲话，倡议打好新冠肺炎疫情防控全球阻击战，有效开展国际联防联控，积极支持国际组织发挥作用，加强国际宏观经济政策协调。",
        #     "question": "结合材料并运用政治生活知识，分析打赢新冠肺炎疫情防控全球阻击战为什么要秉持人类命运共同体理念。",
        #     "standard_answer": "①国家利益是国际关系的决定因素，共同利益是国际合作的基础。秉持人类命运共同体理念，加强国际合作，携手赢得这场人类同重大传染性疾病的斗争，是各国共同利益之所在。\n②和平与发展是当今时代的主题（或：维护世界和平、促进共同发展是我国外交政策的宗旨）。中方秉持人类命运共同体理念，愿向其他国家提供力所能及的援助，为世界经济稳定作出贡献。\n③秉持人类命运共同体理念符合我国外交政策中维护国家主权、安全和发展利益，促进世界和平与发展的目标；符合我国平等互利、和平共处的外交基本准则。\n④在当今的国际社会，维护和保障人权是一项基本道义原则。是否合乎保障人权的要求已成为评判一个集体（无论是政治上的还是经济上的）优劣的重要标准。坚持人类命运共同体理念，坚决打赢疫情防治攻坚战，有利于尊重和保障人权。\n⑤我国是人民民主专政的社会主义国家，人民是国家的主人，坚持人类命运共同体理念，是保障人民主人翁地位的体现。",
        #     "answer": "打赢新冠肺炎疫情防控全球阻击战需要秉持人类命运共同体理念，因为新冠肺炎疫情是全球性的大流行病，影响范围广泛，对全世界构成严重危机和严峻考验。这种理念强调全球的团结和协作，而不是分裂和对抗。只有各国共同努力，才能有效地防控疫情，保护人类的生命安全和健康。此外，这也体现了中国作为一个大国的责任和担当，愿意同其他国家并肩作战、共克时艰，分享防控和救治经验，提供人道主义援助，支持全球抗击疫情。",
        #     "reason": "学生的答案基本上符合标准答案的要求，但是在表述上略显简略，没有完全展开分析，缺少了对国家利益、和平与发展、外交政策宗旨、人权保障等方面的具体阐述。因此，可以给予部分分数。\n1. 对于提到的全球性大流行病和全球团结协作的部分，可以给予部分分数。\n2. 对于提到的中国作为大国的责任和担当，可以给予部分分数。\n3. 由于学生答案没有涉及到国家利益、和平与发展、外交政策宗旨、人权保障等方面的具体分析，扣去相应的分数。",
        #     "score": 5.83
        # },
        {
            "information": "家庭农场是指以家庭成员为主要劳动力，从事农业规模化、集约化、商品化生产经营，并以农业收入为家庭主要收入来源的农业组织形式。2013年中央“一号文件”首次提出发展家庭农场。2019年中央“一号文件”提出“全面深化农村改革，激发乡村发展活力”，并再次强调要坚持家庭经营基础性地位，突出培育家庭农场等新型农业经营主体。\n近年来，我国家庭农场发展迅速，数量已超过87.7万户。据2019年农业农村部信息：\n我国家庭农场大多由小农户升级而来，经营规模在20～200亩之间；家庭农场主要从事种植业、养殖业和种养结合，其中种植类农场有56.1 % 采用了喷灌技术，养殖类农场有近80 % 进行了粪便资源化、综合循环利用和无害化处理；在不少家庭农场中，父辈负责生产，子女负责营销，经营的农产品有以自己名字命名的品牌；全国有36.9 % 的家庭农场加入了农民合作社，参与和分享农机、良种、技术、订单等服务。",
            "question": "结合材料并运用经济知识，说明发展家庭农场对于激发乡村经济活力的积极作用。",
            "standard_answer": "①促进农村土地流转与规模经营，提高农业劳动生产率；\n②促使先进农业技术应用，提高农产品产量和品质；\n③推进农业品牌化、专业化经营，增强农产品市场竞争力；\n④拓宽农民合作社服务范围，推动乡村产业融合发展。",
            "answer": "发展家庭农场在以下几个方面起到了积极作用：1.推动农业规模化、集约化、商品化。2.降低农业生产成本，提高农产品的质量和产量。3.提高农产品的附加值，增加农业收入。4.推动农民合作社的发展。",
            "reason": "Rouge=2*Count(标准答案与待判答案之间相同字符的数量)/(Count(标准答案的字符数量)+Count(待判答案的字符数量))，标准答案与待判答案之间相同字符的数量=55，标准答案的字符数量=101，待判答案的字符数量=94，Rouge=0.564，且待判答案涵盖了标准答案中的四个主要点，即提高农业生产效率、推动农业技术更新、农产品品牌化以及推动农民合作社的发展，但描述简短没有扩展和详细描述。",
            "score": 8
            # }, {
            #     "information": "家庭农场是指以家庭成员为主要劳动力，从事农业规模化、集约化、商品化生产经营，并以农业收入为家庭主要收入来源的农业组织形式。2013年中央“一号文件”首次提出发展家庭农场。2019年中央“一号文件”提出“全面深化农村改革，激发乡村发展活力”，并再次强调要坚持家庭经营基础性地位，突出培育家庭农场等新型农业经营主体。\n近年来，我国家庭农场发展迅速，数量已超过87.7万户。据2019年农业农村部信息：\n我国家庭农场大多由小农户升级而来，经营规模在20～200亩之间；家庭农场主要从事种植业、养殖业和种养结合，其中种植类农场有56.1 % 采用了喷灌技术，养殖类农场有近80 % 进行了粪便资源化、综合循环利用和无害化处理；在不少家庭农场中，父辈负责生产，子女负责营销，经营的农产品有以自己名字命名的品牌；全国有36.9 % 的家庭农场加入了农民合作社，参与和分享农机、良种、技术、订单等服务。",
            #     "question": "结合材料并运用经济知识，说明发展家庭农场对于激发乡村经济活力的积极作用。",
            #     "standard_answer": "①促进农村土地流转与规模经营，提高农业劳动生产率；\n②促使先进农业技术应用，提高农产品产量和品质；\n③推进农业品牌化、专业化经营，增强农产品市场竞争力；\n④拓宽农民合作社服务范围，推动乡村产业融合发展。",
            #     "answer": "这些评价指标在不同的领域有广泛的应用，例如机器翻译、自动摘要和图像描述生成等。它们能够帮助研究人员和开发者对模型进行量化评估、比较不同算法的性能以及进行参数调优。选择合适的评价指标取决于具体任务和需求，因此在实际应用中需根据应用场景选择最适合的指标进行评估。",
            #     "reason": "待判答案与标准答案的关联性极低，标准答案的内容围绕家庭农场和乡村经济等内容，待判答案讲述评价指标的内容，两者基本毫无关联",
            #     "score": 1
            # }, {
            #     "information": "新型冠状病毒肺炎是近百年来人类遭遇的影响范围最广的全球性大流行病，对全世界是一次严重危机和严峻考验。人类生命安全和健康面临重大威胁。\n面对突如其来、来势汹汹的疫情天灾，中国果断打响疫情防控阻击战，把人民生命安全和身体健康放在第一位，采取最全面最严格最彻底的防控措施，取得了抗击疫情重大战略成果。中国始终秉持人类命运共同体理念，肩负大国担当，同其他国家并肩作战、共克时艰，毫无保留同各方分享防控和救治经验，尽己所能向国际社会提供人道主义援助，支持全球抗击疫情。\n2020年3月26日，国家主席习近平出席二十国集团领导人特别峰会并发表《携手抗疫共克时艰》讲话，倡议打好新冠肺炎疫情防控全球阻击战，有效开展国际联防联控，积极支持国际组织发挥作用，加强国际宏观经济政策协调。",
            #     "question": "结合材料并运用政治生活知识，分析打赢新冠肺炎疫情防控全球阻击战为什么要秉持人类命运共同体理念。",
            #     "standard_answer": "①国家利益是国际关系的决定因素，共同利益是国际合作的基础。秉持人类命运共同体理念，加强国际合作，携手赢得这场人类同重大传染性疾病的斗争，是各国共同利益之所在。\n②和平与发展是当今时代的主题（或：维护世界和平、促进共同发展是我国外交政策的宗旨）。中方秉持人类命运共同体理念，愿向其他国家提供力所能及的援助，为世界经济稳定作出贡献。\n③秉持人类命运共同体理念符合我国外交政策中维护国家主权、安全和发展利益，促进世界和平与发展的目标；符合我国平等互利、和平共处的外交基本准则。\n④在当今的国际社会，维护和保障人权是一项基本道义原则。是否合乎保障人权的要求已成为评判一个集体（无论是政治上的还是经济上的）优劣的重要标准。坚持人类命运共同体理念，坚决打赢疫情防治攻坚战，有利于尊重和保障人权。\n⑤我国是人民民主专政的社会主义国家，人民是国家的主人，坚持人类命运共同体理念，是保障人民主人翁地位的体现。",
            #     "answer": "大数据时代是信息爆炸的时代，产生的海量数据具有巨大的价值，但同时也带来了许多安全问题。大数据时代的基本特征包括数据体量巨大、数据类型多样、处理速度快和价值密度低。当前大数据信息安全存在隐私泄露、黑客攻击和数据存储安全等问题。",
            #     "reason": "待判答案与标准答案的关联性极低，题目和标准答案的内容围绕新冠病毒和国际利益、人类命运共同体等内容，待判答案讲述大数据的内容，两者基本毫无关联",
            #     "score": 1
        }]

    example_str = '\n'.join(
        [subjective_question_prompt_example_formmater.format(ques_idx=(idx + 1),
                                                             input_example=subject_example_prompt_formater(
                                                                 i['standard_answer'],
                                                                 i['answer'],
                                                                 rouge(i['standard_answer'],
                                                                       i['answer'])
        ),
            reason=i['reason'], score=i['score'])
            for idx, i in enumerate(questions)])

    input = subjective_question_formmaters[4].format(ques_example=example_str, ques_title=ques_title,
                                                     input_example=subject_example_prompt_formater(standard_answer,
                                                                                                   model_output,
                                                                                                   rouge(
                                                                                                       standard_answer,
                                                                                                       model_output)))
    # print('-' * 10, flush=True)
    # print(input, flush=True)
    response = llm_model.chat(input)
    # print(type(response), response, end='\n', flush=True)

    score = status = 0
    reason = ''
    try:
        # print(response, flush=True)
        output_score_re = re.match(
            subjective_question_response_re_formater, response)

        if output_score_re:
            reason = output_score_re.group(1)
            score = round(float(output_score_re.group(2)), 2)
            status = 1
        else:
            print(f"output_score_re is {output_score_re}", flush=True)
    except Exception as e:
        print(
            f'Exception in get_chatglm_score_by_gaokao: {str(e)}', flush=True)
    return status, score, reason, response


@chatglm_bp.route('/gaokao_create_subjective_question', methods=['POST'])
def gaokao_create_subjective_question():
    st = datetime.datetime.now()
    for ques_idx, ques in enumerate(gaokao_questions):
        ques_title = get_ques_content(ques['question'])

        answer = ques['model_output'].split('【答案】')[1]
        status, score, reason, response = get_chatglm_score_by_gaokao(
            ques_title, ques['standard_answer'], answer)

        try:
            ques_record = Question(ques_tagid=4, ques_title=ques_title, ques_answer=ques['standard_answer'],
                                   ques_score=int(ques['score']), content=response, status=status)
            db.session.add(ques_record)
            db.session.commit()
        except:
            db.session.rollback()

        try:
            as_record = AutoScore(question_id=ques_record.id, answer=answer, llm_score=float(score),
                                  expert_score=round(
                                      float(ques['model_correction_score']
                                            [0]) / float(ques['score']) * 10,
                                      2),
                                  status=status, model_name=model_name, llm_output=response)
            db.session.add(as_record)
            db.session.commit()
        except:
            db.session.rollback()

        et = datetime.datetime.now()
        print(
            f'{ques_idx+1}/{len(gaokao_questions)}, during: {(et-st).seconds}s', flush=True)

    return success_res()


@chatglm_bp.route('/chat', methods=['POST'])
def chat():
    input = request.json.get('input', '')
    response = llm_model.chat(input)
    return jsonify({'output': response})


@chatglm_bp.route('/stream_chat', methods=['POST'])
def stream_chat():
    input = request.json.get('input', '')
    history = request.json.get('history', [])

    history_template = []
    for user, assis in history:
        history_template.append({'role': 'user', 'content': user})
        history_template.append({'role': 'assistant', 'content': assis})

    def generate():
        # history = [{'role':'user', 'content':''}, {'role':'assistant', 'content':''}]
        for response, history, past_key_values in llm_model.model.stream_chat(llm_model.tokenizer, input,
                                                                              history=history_template, temperature=.1,
                                                                              top_k=1, top_p=.1,
                                                                              return_past_key_values=True, ):
            # print(response, flush=True)
            # print(response, flush=True)
            # print('+' * 10, flush=True)
            # print([i.keys() for i in history], flush=True)
            # print('+' * 10, flush=True)
            # print([type(ii) for i in past_key_values for ii in i], flush=True)
            # print('*' * 50, flush=True)
            yield response

        print(history, flush=True)

    return app.response_class(stream_with_context(generate()))


@chatglm_bp.route('/create_questions_from_static_file', methods=['POST'])
def create_questions_from_static_file():
    file_name = request.json.get('file_name', '')
    ques_tagid = request.json.get('ques_tagid', 1)
    file_path = os.path.join(os.getcwd(), 'static', file_name)
    if os.path.exists(file_path):
        with open(file_path, 'r', encoding='utf-8') as f:
            file_data = json.loads(f.read())
        all_res = []
        for content in file_data:
            flag, res = llm_create_objective_question(content, ques_tagid, batch=True,
                                                      datacollection_name=file_name.split('.')[0])
            all_res.append(res)
            if not flag:
                print(flag,content, res, flush=True)
        return success_res(data=all_res)
    else:
        return fail_res(msg="文件不存在")


@chatglm_bp.route('/content_run_formatter', methods=['POST'])
def content_run_formatter():
    content = request.json.get('content', '')
    ques_tagid = request.json.get('ques_tagid', 1)

    flag, res = llm_create_objective_question(
        content, ques_tagid, batch=False, datacollection_name='test_in_python')
    print("结果：", flag, res)
    return success_res()


@chatglm_bp.route('/create_questions_from_static_all_files', methods=['POST', 'GET'])
def create_questions_from_static_all_files():
    file_name = ['entity_summary.json', 'computer_science.json']
    ques_tagids = [1, 3]
    st = datetime.datetime.now()
    for file_name in file_name:
        file_path = os.path.join(os.getcwd(), 'static', file_name)
        with open(file_path, 'r', encoding='utf-8') as f:
            file_data = json.loads(f.read())
        for idx, content in enumerate(file_data):
            for ques_tagid in ques_tagids:
                flag, res = llm_create_objective_question(content, ques_tagid, batch=True,
                                                          datacollection_name=file_name.split('.')[0])
                et = datetime.datetime.now()
            print(
                f'{file_name}: {idx+1}/{len(file_data)}, during: {round((et - st).seconds / 60, 2)}mins', flush=True)
    return success_res()

def llm_create_subjective_question(content, ques_tagid=4, batch=False, datacollection_name=None):
    """
    创建简答题
    params: content: 题干来源材料
    params: ques_tagid: 题型ID，4表示主观题
    params: batch: 是否批量处理
    params: datacollection_name: 数据集合名称
    """
    # 导入需要的模块
    
    input_prompt = question_formmaters[ques_tagid].format(content=content)
    print(' - ' * 20, flush=True)
    # print('主观题input：', input_prompt, flush=True)
    
    response = llm_model.chat(input_prompt)
    
    flag, data = True, []
    try:
        # print('主观题response：', response, flush=True)
        
        # 解析JSON格式的响应
        try:
            response_data = json.loads(response)
        except:
            # 如果直接解析失败，使用正则表达式提取
            response_data = []
            # 提取问题
            question_pattern = r'"问题":\s*"([^"]+)"'
            questions = re.findall(question_pattern, response)
            
            # 提取答案块
            answer_pattern = r'"答案":\s*\[([^\]]+)\]'
            answer_blocks = re.findall(answer_pattern, response, re.DOTALL)
            
            for i, question in enumerate(questions):
                if i < len(answer_blocks):
                    # 解析答案
                    answer_items = []
                    content_score_pattern = r'"内容":\s*"([^"]+)",\s*"得分":\s*(\d+)'
                    matches = re.findall(content_score_pattern, answer_blocks[i], re.DOTALL)
                    
                    for content_match, score_match in matches:
                        answer_items.append({
                            "内容": content_match,
                            "得分": int(score_match)
                        })
                    
                    response_data.append({
                        "问题": question,
                        "满分": 10,
                        "答案": answer_items
                    })
        
        if response_data:
            for item in response_data:
                ques_title = item.get("问题", "")
                max_score = item.get("满分", 10)
                answers = item.get("答案", [])
                
                # 验证题目质量
                if not ques_title or not answers:
                    continue
                
                # 构造答案数据
                answer_data = []
                for answer_item in answers:
                    answer_content = answer_item.get("内容", "")
                    score = answer_item.get("得分", 0)
                    if answer_content and score > 0:
                        answer_data.append({
                            "answer_content": answer_content,
                            "score": score
                        })
                
                # 检查是否有不同分数的答案
                scores = [ans["score"] for ans in answer_data]
                if len(set(scores)) < 2:  # 至少要有2个不同分数的答案
                    continue
                
                # 构建题目数据字典
                question_data = {
                    'ques_title': ques_title,
                    'max_score': max_score,
                    'answers': answer_data,
                    'ques_tagid': ques_tagid,
                    'status': 1  # 有效题目
                }
                
                # 如果是批量模式，将题目存入数据库
                if batch:
                    try:
                        # 创建检查器
                        inspector = inspect(db.engine)
                        
                        # 检查并创建表（确保表存在）
                        if not inspector.has_table('subjective_questions'):
                            print("简答题表不存在，正在创建...")
                            SubjectiveQuestion.__table__.create(db.engine)
                        else:
                            # 检查是否存在相同的题目
                            existing_question = SubjectiveQuestion.query.filter_by(
                                ques_title=ques_title
                            ).first()
                            
                            if existing_question:
                                print(f"简答题已存在，不重复存储: {ques_title}")
                                question_data['id'] = existing_question.id  # 使用已有ID
                                data.append(question_data)
                                continue  # 跳过存储步骤
                        
                        # 将答案数据转换为JSON字符串存储
                        answers_json = json.dumps(answer_data, ensure_ascii=False)
                        
                        # 创建数据库记录
                        new_question = SubjectiveQuestion(
                            ques_title=ques_title,
                            max_score=max_score,
                            answers=answers_json,
                            ques_tagid=ques_tagid,
                            status=1,
                            datacollection_name=datacollection_name
                            # created_at会自动设置为当前时间，不需要显式传入
                        )
                        
                        # 添加到数据库会话
                        db.session.add(new_question)
                        # 提交事务
                        db.session.commit()
                        
                        # 记录数据库ID
                        question_data['id'] = new_question.id
                        print(f"成功保存简答题到数据库，ID: {new_question.id}")
                    except Exception as db_e:
                        # 发生错误时回滚
                        db.session.rollback()
                        print(f"保存简答题到数据库失败: {str(db_e)}")
                        flag = False
                
                # 添加到返回数据
                data.append(question_data)
        else:
            flag, data = False, "主观题解析失败"
            
    except Exception as e:
        print(f"Exception in llm_create_subjective_question: {str(e)}", flush=True)
        flag, data = False, "主观题生成异常"
    
    return flag, data

@chatglm_bp.route('/create_subjective_question', methods=['POST'])
def create_subjective_question():
    content = request.json.get('content', '')
    ques_tagid = request.json.get('ques_tagid', 4)
    batch = request.json.get('batch', False)
    if ques_tagid in question_formmaters:
        flag, res = llm_create_subjective_question(content, ques_tagid, batch=batch, datacollection_name=None)
        if flag:
            return success_res(data=res)
        else:
            return fail_res(msg=f"题目创建出错，{res}")
    else:
        return fail_res(msg="没有该题型自动出题")

def llm_create_dissert_question(content, ques_tagid=5, batch=False, datacollection_name=None):
    """
    params: content: 题干来源材料
    params: ques_tagid: 题型ID，5表示论述题
    params: batch: 是否批量处理
    params: datacollection_name: 数据集合名称
    """
    
    # 设置论述题的prompt
    input_prompt = question_formmaters[ques_tagid].format(content=content)
    
    print(' - ' * 20, flush=True)
    print('input：', input_prompt, flush=True)
    response = llm_model.chat(input_prompt)

    flag, data = True, []
    try:
        print('论述题：', response, flush=True)
        
        # 尝试解析JSON响应
        try:
            questions = json.loads(response)
        except json.JSONDecodeError:
            # 如果直接解析失败，尝试提取JSON格式内容
            json_pattern = r'\[.*\]'  # 匹配JSON数组
            json_match = re.search(json_pattern, response, re.DOTALL)
            
            if json_match:
                json_str = json_match.group(0)
                try:
                    questions = json.loads(json_str)
                except json.JSONDecodeError:
                    raise ValueError("无法解析大模型返回的论述题JSON数据")
            else:
                raise ValueError("在大模型返回中找不到JSON格式的论述题数据")
        
        if not isinstance(questions, list) or len(questions) == 0:
            raise ValueError("大模型返回的论述题格式不正确或为空")
        
        for question_item in questions:
            try:
                # 提取问题和答案
                ques_title = question_item.get("问题", "")
                max_score = question_item.get("满分", 15)  # 论述题默认满分15分
                answers = question_item.get("答案", [])
                
                if not ques_title or not answers:
                    continue
                
                # 处理答案列表
                processed_answers = []
                for answer in answers:
                    answer_content = answer.get("内容", "")
                    score = answer.get("得分", 0)
                    if answer_content and score > 0:
                        processed_answers.append({"answer_content": answer_content, "score": score})
                
                # 确保答案按得分从高到低排序
                processed_answers.sort(key=lambda x: x["score"], reverse=True)
                
                # 检查是否有不同分数的答案
                scores = [ans["score"] for ans in processed_answers]
                if len(set(scores)) < 2:  # 至少要有2个不同分数的答案
                    continue
                
                # 检查问题是否有效
                ques_word_tags = ['论述', '分析', '评价', '探讨', '如何', '为什么', '关系', '影响', '作用', '比较', '说明', '解释', '意义', '价值', '原因', '对策', '途径', '方法', '策略', '挑战', '机遇', '优势', '不足']
                def check_is_dissert_question(content):
                    for tag in ques_word_tags:
                        if tag in content:
                            return True
                    return False
                
                status = 1 if check_is_dissert_question(ques_title) else 0
                
                # 构建题目数据字典
                question_data = {
                    'ques_title': ques_title,
                    'max_score': max_score,
                    'answers': processed_answers,
                    'ques_tagid': ques_tagid,
                    'status': status
                }
                
                if batch:
                    try:
                        # 创建检查器
                        inspector = inspect(db.engine)
                        
                        # 检查并创建表（确保表存在）
                        if not inspector.has_table('dissertation_questions'):
                            print("论述题表不存在，正在创建...")
                            DissertationQuestion.__table__.create(db.engine)
                        else:
                            # 检查是否存在相同的题目
                            existing_question = DissertationQuestion.query.filter_by(
                                ques_title=ques_title
                            ).first()
                            
                            if existing_question:
                                print(f"论述题已存在，不重复存储: {ques_title}")
                                question_data['id'] = existing_question.id  # 使用已有ID
                                data.append(question_data)
                                continue  # 跳过存储步骤
                        
                        # 将答案数据转换为JSON字符串存储
                        answers_json = json.dumps(processed_answers, ensure_ascii=False)
                        
                        # 创建数据库记录
                        new_question = DissertationQuestion(
                            ques_title=ques_title,
                            max_score=max_score,
                            answers=answers_json,
                            ques_tagid=ques_tagid,
                            status=status,
                            datacollection_name=datacollection_name
                            # created_at会自动设置为当前时间，不需要显式传入
                        )
                        
                        # 添加到数据库会话
                        db.session.add(new_question)
                        # 提交事务
                        db.session.commit()
                        
                        # 记录数据库ID
                        question_data['id'] = new_question.id
                        print(f"成功保存论述题到数据库，ID: {new_question.id}")
                    except Exception as db_e:
                        # 发生错误时回滚
                        db.session.rollback()
                        print(f"保存论述题到数据库失败: {str(db_e)}")
                        flag = False
                
                # 添加到返回数据
                data.append(question_data)
            
            except Exception as e:
                print(f"处理单个论述题时出错: {str(e)}", flush=True)
                continue
        
        if not data:
            flag, data = False, "题目完整度不足"
    
    except Exception as e:
        print(f"Exception in llm_create_dissert_question: {str(e)}", flush=True)
        flag, data = False, "自动出题异常"

    return flag, data

@chatglm_bp.route('/content_create_dissert_question', methods=['POST'])
def content_create_dissert_question():
    content = request.json.get('content', '')
    ques_tagid = request.json.get('ques_tagid', 5)
    batch = request.json.get('batch', False)
    
    flag, res = llm_create_dissert_question(content, ques_tagid, batch=batch, datacollection_name=None)
    if flag:
        return success_res(data=res)
    else:
        return fail_res(msg=f"论述题创建出错，{res}")

def llm_auto_judge(answer_content, standard_answer, ques_tagid=1, batch=False, datacollection_name=None):
    """
    params: answer_content: 学生回答内容
    params: standard_answer: 标准答案内容
    """
    # 设置判分的prompt
    input_prompt = auto_judge_fomatters[ques_tagid].format(answer_content=answer_content, standard_answer=standard_answer)
    
    print(' - ' * 20, flush=True)
    print('input：', input_prompt, flush=True)
    response = llm_model.chat(input_prompt)

    flag, data = True, []
    try:
        print('判分结果：', response, flush=True)
        
        # 尝试解析JSON响应
        try:
            judge_result = json.loads(response)
        except json.JSONDecodeError:
            # 如果直接解析失败，尝试提取JSON格式内容
            json_pattern = r'\{.*\}'  # 匹配JSON对象
            json_match = re.search(json_pattern, response, re.DOTALL)
            
            if json_match:
                json_str = json_match.group(0)
                try:
                    judge_result = json.loads(json_str)
                except json.JSONDecodeError:
                    raise ValueError("无法解析大模型返回的判分JSON数据")
            else:
                raise ValueError("在大模型返回中找不到JSON格式的判分数据")
        
        # 验证判分结果格式
        if not isinstance(judge_result, dict) or "10 次判分" not in judge_result or "最终得分" not in judge_result:
            raise ValueError("大模型返回的判分格式不正确")
        
        if batch:
            try:
                # 保存到数据库
                aj = AutoQuestion(
                    answer_content=answer_content, 
                    standard_answer=standard_answer,
                    output_chat=response, 
                    ques_tagid=6,  # 假设判分题的tagid为6
                    judge_result=json.dumps(judge_result, ensure_ascii=False),
                    total_score=judge_result.get("最终得分", 0),
                    model_name=model_name, 
                    datacollection_name=datacollection_name
                )
                db.session.add(aj)
                db.session.commit()
            except Exception as e:
                print(f"保存判分结果到数据库失败: {str(e)}", flush=True)
                db.session.rollback()
        
        # 添加到返回数据
        data = judge_result
        
    except Exception as e:
        print(f"Exception in llm_auto_judge: {str(e)}", flush=True)
        flag, data = False, "自动判分异常"

    return flag, data

@chatglm_bp.route('/auto_judge', methods=['POST'])
def auto_judge():
    answer_content = request.json.get('answer_content', '')
    standard_answer = request.json.get('standard_answer', '')
    ques_tagid = request.json.get('ques_tagid', 1)
    
    flag, res = llm_auto_judge(answer_content, standard_answer, ques_tagid, batch=False, datacollection_name=None)
    if flag:
        return success_res(data=res)
    else:
        return fail_res(msg=f"自动判分出错，{res}")

def llm_create_multi_choice(content, ques_tagid=2, batch=False, datacollection_name=None):
    """
    生成多选题的函数
    params: content: 题干来源材料
    params: ques_tagid: 题型: 1选择(单选) 2选择(多选) 3填空
    """
    
    input_prompt = question_formmaters[ques_tagid].format(content=content)

    print(' - ' * 20, flush=True)
    print('input：', input_prompt, flush=True)
    response = llm_model.chat(input_prompt)
    
    flag, data = True, []
    try:
        print('客观题：', response, flush=True)
        # 解析多选题格式
        try:
            questions = json.loads(response)
        except json.JSONDecodeError:
            # 尝试正则解析非标准JSON格式
            pattern = r'"题目":\s*"(.*?)",\s*"选项":\s*\{(.*?)\},\s*"答案":\s*\[(.*?)\]'
            matches = re.finditer(pattern, response, re.DOTALL)
            questions = []
            
            for match in matches:
                if match:
                    ques_title = match.group(1)
                    options_str = match.group(2)
                    answers_str = match.group(3)
                    
                    # 解析选项
                    options_pattern = r'"(\w)":\s*"(.*?)"'
                    options_matches = re.findall(options_pattern, options_str)
                    options = {option[0]: option[1] for option in options_matches}
                    
                    # 解析答案列表
                    answers_pattern = r'"(\w)"'
                    answers = re.findall(answers_pattern, answers_str)
                    
                    questions.append({
                        "题目": ques_title,
                        "选项": options,
                        "答案": answers
                    })
        
        if not questions:
            flag, data = False, "题目完整度不足"
            return flag, data
            
        for question in questions:
            ques_title = question.get("题目", "")
            options = question.get("选项", {})
            answers = question.get("答案", [])
            
            if not ques_title or not options or len(answers) not in [2, 3]:
                continue
            
            # 检查问题是否有效
            ques_word_tags = ['哪些', '多选', '正确的', '包括']
            def check_is_multi_question(content):
                for tag in ques_word_tags:
                    if tag in content:
                        return True
                return False
            
            status = 0
            if check_is_multi_question(ques_title) and all(answer in options for answer in answers):
                status = 1
            
            # 构建题目数据字典
            question_data = {
                'ques_title': ques_title, 
                'ques_options': options,
                'ques_answer': answers,
                'ques_tagid': ques_tagid,
                'status': status
            }
            
            # 如果是批量模式，将题目存入数据库
            if batch:
                try:
                    # 创建检查器
                    inspector = inspect(db.engine)
                    
                    # 检查并创建表（确保表存在）
                    if not inspector.has_table('multi_choice_questions'):
                        print("多选题表不存在，正在创建...")
                        MultiChoiceQuestion.__table__.create(db.engine)
                    else:
                        # 检查是否存在相同的题目
                        existing_question = MultiChoiceQuestion.query.filter_by(
                            ques_title=ques_title
                        ).first()
                        
                        if existing_question:
                            print(f"多选题已存在，不重复存储: {ques_title}")
                            question_data['id'] = existing_question.id  # 使用已有ID
                            data.append(question_data)
                            continue  # 跳过存储步骤
                    
                    # 将选项和答案转换为JSON字符串存储
                    options_json = json.dumps(options, ensure_ascii=False)
                    answers_json = json.dumps(answers, ensure_ascii=False)
                    
                    # 创建数据库记录
                    new_question = MultiChoiceQuestion(
                        ques_title=ques_title,
                        ques_options=options_json,
                        ques_answer=answers_json,
                        ques_tagid=ques_tagid,
                        status=status,
                        datacollection_name=datacollection_name
                        # created_at会自动设置为当前时间，不需要显式传入
                    )
                    
                    # 添加到数据库会话
                    db.session.add(new_question)
                    # 提交事务
                    db.session.commit()
                    
                    # 记录数据库ID
                    question_data['id'] = new_question.id
                    print(f"成功保存多选题到数据库，ID: {new_question.id}")
                except Exception as db_e:
                    # 发生错误时回滚
                    db.session.rollback()
                    print(f"保存多选题到数据库失败: {str(db_e)}")
                    flag = False
            
            # 添加到返回数据
            data.append(question_data)
        
        if not data:
            flag, data = False, "题目完整度不足"
    
    except Exception as e:
        print(f"Exception in llm_create_multi_choice: {str(e)}", flush=True)
        flag, data = False, "自动出题异常"

    return flag, data

@chatglm_bp.route('/content_create_multi_choice', methods=['POST'])
def content_create_multi_choice():
    content = request.json.get('content', '')
    ques_tagid = request.json.get('ques_tagid', 2)
    batch = request.json.get('batch', False)
    
    flag, res = llm_create_multi_choice(content, ques_tagid, batch=batch, datacollection_name=None)
    if flag:
        return success_res(data=res)
    else:
        return fail_res(msg=f"多选题创建出错，{res}")
    
def llm_create_single_choice(content, ques_tagid=1, batch=False, datacollection_name=None):
    """
    生成单选题的函数
    params: content: 题干来源材料
    params: ques_tagid: 题型: 1选择(单选) 2选择(多选) 3填空
    """
    input_prompt = question_formmaters[ques_tagid].format(content=content)

    print(' - ' * 20, flush=True)
    print('input：', input_prompt, flush=True)
    response = llm_model.chat(input_prompt)

    flag, data = True, []
    try:
        print('客观题：', response, flush=True)
        # 解析单选题格式
        try:
            questions = json.loads(response)
        except json.JSONDecodeError:
            # 尝试正则解析非标准JSON格式
            pattern = r'"问题":\s*"(.*?)",\s*"选项":\s*\{(.*?)\},\s*"答案":\s*"(.*?)"'
            matches = re.finditer(pattern, response, re.DOTALL)
            questions = []
            
            for match in matches:
                if match:
                    ques_title = match.group(1)
                    options_str = match.group(2)
                    answer = match.group(3)
                    
                    # 解析选项
                    options_pattern = r'"(\w)":\s*"(.*?)"'
                    options_matches = re.findall(options_pattern, options_str)
                    options = {option[0]: option[1] for option in options_matches}
                    
                    questions.append({
                        "问题": ques_title,
                        "选项": options,
                        "答案": answer
                    })
        
        if not questions:
            flag, data = False, "题目完整度不足"
            return flag, data
            
        for question in questions:
            ques_title = question.get("问题", "")
            options = question.get("选项", {})
            answer = question.get("答案", "")
            
            # 检查题目是否有效：题目不为空，选项是A-D中的至少4个，答案是A-D中的一个
            if not ques_title or len(options) < 4 or answer not in ['A', 'B', 'C', 'D']:
                continue
            
            # 检查问题是否有效（是否包含关键信息提示）
            ques_word_tags = ['【】', '什么', '哪个', '何时', '何地', '谁']
            def check_is_single_question(content):
                for tag in ques_word_tags:
                    if tag in content:
                        return True
                return False
            
            status = 0
            if check_is_single_question(ques_title) and answer in options:
                status = 1
            
            # 构建题目数据字典
            question_data = {
                'ques_title': ques_title, 
                'ques_options': options,
                'ques_answer': answer,
                'ques_tagid': ques_tagid,
                'status': status
            }
            
            # 如果是批量模式，将题目存入数据库
            if batch:
                try:
                    # 创建检查器
                    inspector = inspect(db.engine)
                    
                    # 检查并创建表（确保表存在）
                    if not inspector.has_table('single_choice_questions'):
                        print("表不存在，正在创建...")
                        SingleChoiceQuestion.__table__.create(db.engine)
                    else:
                        # 检查是否存在相同的题目
                        existing_question = SingleChoiceQuestion.query.filter_by(
                            ques_title=ques_title
                        ).first()
                        
                        if existing_question:
                            print(f"题目已存在，不重复存储: {ques_title}")
                            question_data['id'] = existing_question.id  # 使用已有ID
                            data.append(question_data)
                            continue  # 跳过存储步骤

                    # 将选项字典转换为JSON字符串存储
                    options_json = json.dumps(options, ensure_ascii=False)
                    
                    # 创建数据库记录
                    new_question = SingleChoiceQuestion(
                        ques_title=ques_title,
                        ques_options=options_json,
                        ques_answer=answer,
                        ques_tagid=ques_tagid,
                        status=status,
                        datacollection_name=datacollection_name
                        # created_at会自动设置为当前时间，不需要显式传入
                    )
                    
                    # 添加到数据库会话
                    db.session.add(new_question)
                    # 提交事务
                    db.session.commit()
                    
                    # 记录数据库ID
                    question_data['id'] = new_question.id
                    print(f"成功保存单选题到数据库，ID: {new_question.id}")
                except Exception as db_e:
                    # 发生错误时回滚
                    db.session.rollback()
                    print(f"保存单选题到数据库失败: {str(db_e)}")
                    flag = False
            
            # 添加到返回数据
            data.append(question_data)
        
        if not data:
            flag, data = False, "题目完整度不足"
    
    except Exception as e:
        print(f"Exception in llm_create_single_choice: {str(e)}", flush=True)
        flag, data = False, "自动出题异常"

    return flag, data


@chatglm_bp.route('/content_create_single_choice', methods=['POST'])
def content_create_single_choice():
    content = request.json.get('content', '')
    ques_tagid = request.json.get('ques_tagid', 1)  # 默认为单选题
    batch = request.json.get('batch', False)
    
    flag, res = llm_create_single_choice(content, ques_tagid, batch=batch, datacollection_name=None)
    if flag:
        return success_res(data=res)
    else:
        return fail_res(msg=f"单选题创建出错，{res}")
    
def llm_create_fill_blank(content, ques_tagid=3, batch=False, datacollection_name=None):
    """
    生成填空题的函数
    params: content: 题干来源材料
    params: ques_tagid: 题型: 1选择(单选) 2选择(多选) 3填空
    """
    
    input_prompt = question_formmaters[ques_tagid].format(content=content)

    print(' - ' * 20, flush=True)
    print('input：', input_prompt, flush=True)
    response = llm_model.chat(input_prompt)

    flag, data = True, []
    try:
        print('填空题：', response, flush=True)
        # 解析填空题格式
        try:
            questions = json.loads(response)
        except json.JSONDecodeError:
            # 尝试正则解析非标准JSON格式
            pattern = r'"文本片段":\s*"(.*?)",\s*"关键信息":\s*\[(.*?)\]'
            matches = re.finditer(pattern, response, re.DOTALL)
            questions = []
            
            for match in matches:
                if match:
                    text_fragment = match.group(1)
                    keywords_str = match.group(2)
                    
                    # 解析关键信息列表
                    keywords_pattern = r'"(.*?)"'
                    keywords = re.findall(keywords_pattern, keywords_str)
                    
                    questions.append({
                        "文本片段": text_fragment,
                        "关键信息": keywords
                    })
        
        if not questions:
            flag, data = False, "题目完整度不足"
            return flag, data
            
        for question in questions:
            text_fragment = question.get("文本片段", "")
            keywords = question.get("关键信息", [])
            
            # 检查题目是否有效：文本片段不为空，关键信息数量在1-3个之间，且包含【】符号
            if not text_fragment or len(keywords) < 1 or len(keywords) > 3 or "【】" not in text_fragment:
                continue
            
            # 检查关键信息是否有效
            def check_is_valid_keywords(keywords_list):
                # 检查是否所有关键词都是非空字符串
                return all(isinstance(k, str) and k.strip() != "" for k in keywords_list)
            
            status = 0
            if check_is_valid_keywords(keywords):
                status = 1
            
            # 构建题目数据字典
            question_data = {
                'ques_title': text_fragment, 
                'ques_keywords': keywords,
                'ques_tagid': ques_tagid,
                'status': status
            }
            
            # 如果是批量模式，将题目存入数据库
            if batch:
                try:
                    # 创建检查器
                    inspector = inspect(db.engine)
                    
                    # 检查并创建表（确保表存在）
                    if not inspector.has_table('fill_blank_questions'):
                        print("填空题表不存在，正在创建...")
                        FillBlankQuestion.__table__.create(db.engine)
                    else:
                        # 检查是否存在相同的题目
                        existing_question = FillBlankQuestion.query.filter_by(
                            ques_title=text_fragment
                        ).first()
                        
                        if existing_question:
                            print(f"填空题已存在，不重复存储: {text_fragment}")
                            question_data['id'] = existing_question.id  # 使用已有ID
                            data.append(question_data)
                            continue  # 跳过存储步骤
                    
                    # 将关键信息列表转换为JSON字符串存储
                    keywords_json = json.dumps(keywords, ensure_ascii=False)
                    
                    # 创建数据库记录
                    new_question = FillBlankQuestion(
                        ques_title=text_fragment,
                        ques_keywords=keywords_json,
                        ques_tagid=ques_tagid,
                        status=status,
                        datacollection_name=datacollection_name
                        # created_at会自动设置为当前时间，不需要显式传入
                    )
                    
                    # 添加到数据库会话
                    db.session.add(new_question)
                    # 提交事务
                    db.session.commit()
                    
                    # 记录数据库ID
                    question_data['id'] = new_question.id
                    print(f"成功保存填空题到数据库，ID: {new_question.id}")
                except Exception as db_e:
                    # 发生错误时回滚
                    db.session.rollback()
                    print(f"保存填空题到数据库失败: {str(db_e)}")
                    flag = False
            
            # 添加到返回数据
            data.append(question_data)
        
        if not data:
            flag, data = False, "题目完整度不足"
    
    except Exception as e:
        print(f"Exception in llm_create_fill_blank: {str(e)}", flush=True)
        flag, data = False, "自动出题异常"

    return flag, data

@chatglm_bp.route('/content_create_fill_blank', methods=['POST'])
def content_create_fill_blank():
    content = request.json.get('content', '')
    ques_tagid = request.json.get('ques_tagid', 3)  # 默认为填空题
    batch = request.json.get('batch', False)
    
    flag, res = llm_create_fill_blank(content, ques_tagid, batch=batch, datacollection_name=None)
    if flag:
        return success_res(data=res)
    else:
        return fail_res(msg=f"填空题创建出错，{res}")